package com.atguigu.utils;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author zhengkaiyuan
 * @version 1.0
 * @create 2021-06-26 10:00
 */
public class CascadeDownLoadUtils {

    /**
     * @param fileName 指定的上传文件名称
     * @param list     指定的上传数据
     * @param filePath 指定的上传文件路径
     * @param <T>
     * @return 带返回文件名带有下载条数
     * @throws Exception
     */
    public static <T> String createExcelToName(String fileName, List<T> list, String filePath) throws Exception {

        System.out.println("fileName = " + fileName + "开始下载");
        //在内存中创建一个Excel文件
        XSSFWorkbook workbook = new XSSFWorkbook();
        //创建sheet页并添加数据
        creatSheetAndFillerData(workbook, list, fileName);
        //拼接地址 防止文件名称重复
        String realFileName = fileName + "-" + list.size() + "-item";
        String address = filePath + File.separator + realFileName + ".xlsx";
        //通过输出流将workbook对象下载到磁盘
        FileOutputStream out = new FileOutputStream(address);
        workbook.write(out);
        out.flush();//刷新
        out.close();//关闭
        workbook.close();

        return realFileName + ".xlsx";
    }


    /**
     * 创建sheet页面，填充数据
     *
     * @param workbook excel表
     * @param list     填充的数据
     * @param fileName 文件名称
     * @param <T>
     * @throws Exception
     */
    private static <T> void creatSheetAndFillerData(XSSFWorkbook workbook, List<T> list, String fileName) throws Exception {
        if (list == null || list.size() == 0) {
            return;
        }
        //创建工作表，指定工作表名称
        XSSFSheet sheet = workbook.createSheet(fileName);
        //创建行，0表示第一行
        XSSFRow row = sheet.createRow(0);
        //获取当前集合中数据的类型,以及对应的类元信息
        Class<?> instance = list.get(0).getClass();
        //获取类元信息
        List<String> meta = getMeta(instance);
        //第一行初始化表头信息
        for (int i = 0; i < meta.size(); i++) {
            row.createCell(i).setCellValue(meta.get(i));
        }
        //封装成map
        List<Map<String, String>> maps = wrapMap(list);
        //查看当前对象中是否存在集合类型对象，有就重新创建sheet页并填充数据
        for (T t : list) {
            for (String s : meta) {
                Field travelItems = instance.getDeclaredField(s);
                travelItems.setAccessible(true);
                if (judge(travelItems)) {
                    String fileNameSheet = instance.getName() + "_" + String.valueOf(UUID.randomUUID());
                    List newSheet = (List) travelItems.get(t);
                    creatSheetAndFillerData(workbook, newSheet,fileNameSheet);
                }
            }
        }
        //定义每一行的索引位置(即添加数据的索引位置)
        int index = 1;
        //循环向excel中添加数据
        for (Map<String, String> rowData : maps) {
            row = sheet.createRow(index++);
            fillerDate(row, rowData, meta);
        }
    }


    /**
     * @param row         为从第二行添加数据的行号
     * @param rowData     为这一行需要添加的数据
     * @param originTitle 获取第一行的表头信息
     */
    private static void fillerDate(XSSFRow row, Map<String, String> rowData, List<String> originTitle) {
        for (int i = 0; i < originTitle.size(); i++) {
            String value = rowData.get(originTitle.get(i));
            row.createCell(i).setCellValue(value);
        }
    }


    /**
     * @param list 需要封装成map的集合
     */
    private static <T> List<Map<String, String>> wrapMap(List<T> list) throws Exception {
        List<Map<String, String>> maps = new ArrayList<>();
        if (list.size() == 0) {
            throw new RuntimeException("没有数据，无法下载！");
        } else {
            //获取当前集合中数据的类型,以及对应的类元信息
            Class<?> instance = list.get(0).getClass();
            List<String> meta = getMeta(instance);
            for (T item : list) {
                Map<String, String> valueMap = new HashMap<>();
                for (String filed : meta) {
                    Field field = instance.getDeclaredField(filed);
                    field.setAccessible(true);
                    Object value = field.get(item);
                    if (value == null) {
                        valueMap.put(filed, "暂无");
                    }
                    //如果为集合，则key,value相等
                    else if (judge(field)) {
                        valueMap.put(filed, filed);
                    } else {
                        valueMap.put(filed, value.toString());
                    }
                }
                maps.add(valueMap);
            }
            return maps;
        }
    }


    /**
     * 获取类元信息
     *
     * @param instance
     * @return
     */
    private static List<String> getMeta(Class<?> instance) {
        //获取当前对象的所有类元信息
        Field[] declaredFields = instance.getDeclaredFields();
        //定义返回数组
        List<String> meta = new ArrayList<>();
        int index = 0;
        //遍历当前字段的名称
        for (Field declaredField : declaredFields) {
            String fieldName = declaredField.getName();
            if (fieldName.contains("img")) {
                continue;
            } else {
                meta.add(fieldName);
            }
        }
        return meta;
    }

    /**
     * 判断当前是否为参数化泛型
     *
     * @param declaredField
     * @return
     */
    private static boolean judge(Field declaredField) {
        if (declaredField.getGenericType() instanceof ParameterizedType) {
            Type genericType = declaredField.getGenericType();
            //ParameterizedType就是参数化类型
            ParameterizedType pt = (ParameterizedType) genericType;
            //getRawType()返回Tpye，得到“<>”前面的类型，比如List<String>
            if (pt.getRawType().equals(List.class)) {
                return true;
            }
        }
        return false;
    }

}
